package com.hss.thread;
//https://github.com/yangchong211/YCBlogs/blob/master/java/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/07.%E6%AD%BB%E9%94%81%E7%9A%84%E5%8F%91%E7%94%9F%EF%BC%8C%E5%AE%9A%E4%BD%8D%E4%B8%8E%E4%BF%AE%E5%A4%8D.md

/**
 1.代码模拟产生死锁
 1.1 代码描述

 启动两个线程，设置两个线程监听对象obj1 、obj2。
 线程1启动的时候，先获取obj1锁，暂停1秒，然后获取obj2锁。
 线程2启动时，先获取obj2，再获取obj1
 当线程2启动的时候获取obj2成功，然后去获取obj1的时候，obj1被线程1占用，此时就等待。
    线程1秒后去获取obj2，此时obj2锁被线程2握着，产生死锁，互相无法获取

 1.2 产生死锁代码

 代码如下所示
 这种方法将直接导致死锁
         private final Object obj1 = new Object();
         private final Object obj2 = new Object();
         private void test1() {
         new Thread(){
        @Override
        public void run() {
        synchronized (obj1){
        try {
        System.out.println("yc---Thread1 obj1");
        Thread.sleep(1000);
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
        synchronized (obj2){
        System.out.println("yc---Thread1 obj2");
        }
        }
        }
        }.start();
         new Thread(){
        @Override
        public void run() {
        synchronized (obj2){
        System.out.println("yc---Thread2 obj2");
        synchronized (obj1){
        System.out.println("yc---Thread2 obj1");
        }
        }
        }
        }.start();
         }

 //打印结果：
 10-18 17:11:34.255 30427-30478/com.yc.cn.ycbaseadapter I/System.out: yc---Thread1 obj1
 10-18 17:11:34.257 30427-30479/com.yc.cn.ycbaseadapter I/System.out: yc---Thread2 obj2

 1.3 死锁发生的场景

 死锁不仅仅是在线程之间会发生，存在资源独占的进程之间同样也可能出现死锁。大多是聚焦在多线程场景中的死锁，指两个或多个线程之间，由于互相持有对方需要的锁，而永久处于阻塞的状态。

 2.Java中导致死锁的原因
 2.1 造成死锁原因

 在申请锁时发生了交叉闭环申请。即线程在获得了锁1并且没有释放的情况下去申请锁2，这时，另一个线程已经获得了锁2，在释放锁2之前又要先获得锁1，因此闭环发生，陷入死锁循环。

 2.2 死锁的危害

 从上面死锁代码案例可以知道，当发生死锁的时候，导致彼此一直处于等待之中，而导致代码无法执行下去。只能重启，后果比较严重！
 在死锁时，线程间相互等待资源，而又不释放自身的资源，导致无穷无尽的等待，其结果是系统任务永远无法执行完成。系统发生死锁现象不仅浪费大量的系统资源，甚至导致整个系统崩溃，带来灾难性后果。
 .出现死锁需要满足条件
 3.1 死锁问题条件

 互斥条件：一个资源每次只能被一个线程使用。
 请求与保持条件：一个进程因请求资源而阻塞时，对已获得的资源保持不放。
 不剥夺条件：进程已获得的资源，在未使用完之前，不能强行剥夺。
 循环等待条件：若干进程之间形成一种头尾相接的循环等待资源关系。

 3.2 如何预防死锁

 死锁发生时的四个必要条件，只要破坏这四个必要条件中的任意一个条件，死锁就不会发生。这就为我们解决死锁问题提供了可能。一般地，解决死锁的方法分为死锁的预防，避免，检测[定位死锁的位置]与恢复三种（注意：死锁的检测与恢复是一个方法）。 锁的预防是保证系统不进入死锁状态的一种策略。它的基本思想是要求进程申请资源时遵循某种协议，从而打破产生死锁的四个必要条件中的一个或几个，保证系统不会进入死锁状态。
 打破互斥条件。即允许进程同时访问某些资源。但是，有的资源是不允许被同时访问的，像打印机等等，这是由资源本身的属性所决定的。所以，这种办法并无实用价值。
 打破不可抢占条件。即允许进程强行从占有者那里夺取某些资源。就是说，当一个进程已占有了某些资源，它又申请新的资源，但不能立即被满足时，它必须释放所占有的全部资源，以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难，会降低系统性能。
 打破占有且申请条件。可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足，则不分配任何资源，此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时，才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源，所以不会发生占有资源又申请资源的现象，因此不会发生死锁。但是，这种策略也有如下缺点：
 在许多情况下，一个进程在执行之前不可能知道它所需要的全部资源。这是由于进程在执行时是动态的，不可预测的；
 资源利用率低。无论所分资源何时用到，一个进程只有在占有所需的全部资源后才能执行。即使有些资源最后才被该进程用到一次，但该进程在生存期间却一直占有它们，造成长期占着不用的状况。这显然是一种极大的资源浪费；
 降低了进程的并发性。因为资源有限，又加上存在浪费，能分配到所需全部资源的进程个数就必然少了。
 打破循环等待条件，实行资源有序分配策略。采用这种策略，即把资源事先分类编号，按号分配，使进程在申请，占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源，才能申请大号资源，就不会产生环路，从而预防了死锁。这种策略与前面的策略相比，资源的利用率和系统吞吐量都有很大提高，但是也存在以下缺点：
 限制了进程对资源的请求，同时给系统中所有资源合理编号也是件困难事，并增加了系统开销；
 为了遵循按编号申请的次序，暂不使用的资源也需要提前申请，从而增加了进程对资源的占用时间。

 4.死锁诊断步骤
 4.1 如何定位死锁

 定位死锁最常用的工具就是利用jstack等工具获取线程栈，然后定位相互之间的依赖关系，进而找到死锁。如果是比较明显的死锁，往往jstack工具就能直接定位，类似JConsole甚至可以在图形界面进行有限的死锁检测。
 如果程序运行时发生了死锁，绝大多数情况下都是无法在线解决的，只能重启、修正程序本身问题。所以，代码开发阶段相互审查，或者利用工具进行预防性排查，也是很重要的。
 1.首先，可以使用 jps 或者系统的 ps 命令、任务管理器等工具，确定进程 ID。
 2.调用 jstack 获取线程栈：jstack your-pid
 3.然后看看日志，思考怎么用studio将日志打印出来呢？

 5.死锁修复解决方案
 5.1 死锁修复方案

 如果在死锁检查时发现了死锁情况，那么就要努力消除死锁，使系统从死锁状态中恢复过来。消除死锁的几种方式：
 最简单、最常用的方法就是进行系统的重新启动，不过这种方法代价很大，它意味着在这之前所有的进程已经完成的计算工作都将付之东流，包括参与死锁的那些进程，以及未参与死锁的进程；
 撤消进程，剥夺资源。终止参与死锁的进程，收回它们占有的资源，从而解除死锁。这时又分两种情况：一次性撤消参与死锁的全部进程，剥夺全部资源；或者逐步撤消参与死锁的进程，逐步收回死锁进程占有的资源。一般来说，选择逐步撤消的进程时要按照一定的原则进行，目的是撤消那些代价最小的进程，比如按进程的优先级确定进程的代价；考虑进程运行时的代价和与此进程相关的外部作业的代价等因素；
 进程回退策略，即让参与死锁的进程回退到没有发生死锁前某一点处，并由此点处继续执行，以求再次执行时不再发生死锁。虽然这是个较理想的办法，但是操作起来系统开销极大，要有堆栈这样的机构记录进程的每一步变化，以便今后的回退，有时这是无法做到的。


 */


public class Deadlock2 {
}
